use std::{any::TypeId, future::Future, marker::PhantomData, pin::Pin};

use async_trait::async_trait;

use crate::{
    error::InvokeError,
    reflect::Reflect,
    ty::{Object, TypeInfoFn, Value},
};

pub struct Input {
    pub name: String,
    pub ty: TypeInfoFn,
}

pub trait Callable {
    fn call(&self, value: Vec<Value>) -> Result<Option<Object>, InvokeError>;
}

#[async_trait]
pub trait AsyncCallable {
    async fn async_call(&self, value: Vec<Value>) -> Result<Option<Object>, InvokeError>;
}

#[async_trait]
pub trait TraitAsyncCallable {
    async fn async_call(&self, value: Vec<Value>) -> Result<Option<Object>, InvokeError>;
}

pub struct SyncFunctionWrapper(pub Box<dyn Callable>);

pub struct AsyncFunctionWrapper(pub Box<dyn AsyncCallable + Send + Sync>);

pub struct TraitAsyncFunctionWrapper(pub Box<dyn TraitAsyncCallable + Send + Sync>);

impl Callable for SyncFunctionWrapper {
    fn call(&self, value: Vec<Value>) -> Result<Option<Object>, InvokeError> {
        self.0.call(value)
    }
}

#[async_trait]
impl AsyncCallable for AsyncFunctionWrapper {
    async fn async_call(&self, value: Vec<Value>) -> Result<Option<Object>, InvokeError> {
        self.0.async_call(value).await
    }
}

#[async_trait]
impl TraitAsyncCallable for TraitAsyncFunctionWrapper {
    async fn async_call(&self, value: Vec<Value>) -> Result<Option<Object>, InvokeError> {
        self.0.async_call(value).await
    }
}

pub enum FunctionWrapper {
    Sync(SyncFunctionWrapper),
    Async(AsyncFunctionWrapper),
    TraitAsync(TraitAsyncFunctionWrapper),
}

impl FunctionWrapper {
    pub fn sync_function(callable: Box<dyn Callable>) -> Self {
        Self::Sync(SyncFunctionWrapper(callable))
    }
    pub fn async_function(callable: Box<dyn AsyncCallable + Send + Sync>) -> Self {
        Self::Async(AsyncFunctionWrapper(callable))
    }
    pub fn trait_sync_function(callable: Box<dyn TraitAsyncCallable + Send + Sync>) -> Self {
        Self::TraitAsync(TraitAsyncFunctionWrapper(callable))
    }
}

pub type TypeIdFn = fn() -> TypeId;

pub type GetFunctionMetaFn = fn() -> FunctionMeta;

pub struct FunctionSubmitItem {
    pub get_function_meta_fn: GetFunctionMetaFn,
}

inventory::collect!(FunctionSubmitItem);

pub struct FunctionMeta {
    pub class_id: Option<TypeId>,
    pub type_id: Option<TypeId>,
    pub module: String,
    pub trait_name: Option<String>,
    pub ident: String,
    pub inputs: Vec<Input>,
    pub output: Option<TypeInfoFn>,
    pub function: Option<FunctionWrapper>,
    pub docs: Option<String>,
    pub attrs: Vec<String>,
}

pub type RetFn0<R = ()> = fn() -> R;
impl<R: Reflect> Callable for RetFn0<R> {
    fn call(&self, value: Vec<Value>) -> Result<Option<Object>, InvokeError> {
        if !value.is_empty() {
            return Err(InvokeError::ArgumentsNotMatched);
        }
        let r = self().try_into();
        match r {
            Ok(r) => Ok(Some(r)),
            Err(e) => Err(InvokeError::InputConvert(e)),
        }
    }
}

pub struct RetAsyncFn<R, F> {
    pub func: F,
    pub _marker: PhantomData<R>,
}

impl<R, F> RetAsyncFn<R, F> {
    pub fn new(func: F) -> Self {
        Self {
            func,
            _marker: PhantomData,
        }
    }
}

#[allow(type_alias_bounds)]
pub type AsyncRetFn0<O, F: Future> = RetAsyncFn<O, fn() -> F>;

#[async_trait]
impl<O: Reflect, F: Future<Output = O> + Send + Sync> AsyncCallable for RetFn0<F> {
    async fn async_call(&self, value: Vec<Value>) -> Result<Option<Object>, InvokeError> {
        if !value.is_empty() {
            return Err(InvokeError::ArgumentsNotMatched);
        }
        let r = self().await.try_into();
        match r {
            Ok(r) => Ok(Some(r)),
            Err(e) => Err(InvokeError::InputConvert(e)),
        }
    }
}

pub type TraitAsyncRetFn0<O> = fn() -> Pin<Box<dyn Future<Output = O> + Send>>;
#[async_trait]
impl<O: Reflect> TraitAsyncCallable for TraitAsyncRetFn0<O> {
    async fn async_call(&self, value: Vec<Value>) -> Result<Option<Object>, InvokeError> {
        if !value.is_empty() {
            return Err(InvokeError::ArgumentsNotMatched);
        }
        let r = self().await.try_into();
        match r {
            Ok(r) => Ok(Some(r)),
            Err(e) => Err(InvokeError::InputConvert(e)),
        }
    }
}

// 定义 impl Callable
macro_rules! impl_ret_callable {
    ($c:expr, $name:ident, $($param:ident : $var:ident),*) => {
        pub type $name<$( $param ),*, R = ()> = fn($($param),*) -> R;

        impl<$($param: Reflect,)* R: Reflect> Callable for $name<$($param,)* R> {
            fn call(&self, value: Vec<Value>) -> Result<Option<Object>, InvokeError> {
                if value.len() != $c {
                    return Err(InvokeError::ArgumentsNotMatched);
                }
                let mut iter = value.into_iter();

                $(
                    let $var = iter.next().ok_or(InvokeError::ArgumentsNotMatched)?;
                    let $var = $param::downcast($var).map_err(InvokeError::InputConvert)?;
                )*

                let r = self($($var),*).try_into();
                match r {
                    Ok(r) => Ok(Some(r)),
                    Err(e) => Err(InvokeError::InputConvert(e)),
                }
            }
        }
    };
}

// 定义 impl Callable
use paste::paste;
macro_rules! impl_ret_async_callable {
    ($c:expr, $name:ident, $($param:ident : $var:ident),*) => {
        paste! {
            #[allow(type_alias_bounds)]
            pub type [<Async $name>]<$($param,)* O, F: Future> = RetAsyncFn<O, fn($($param,)*) -> F>;
        }

        #[async_trait]
        impl<$($param: Reflect,)* O: Reflect, R: Future<Output = O> + Send + Sync> AsyncCallable for $name<$($param,)* R> {
            async fn async_call(&self, value: Vec<Value>) -> Result<Option<Object>, InvokeError> {
                if value.len() != $c {
                    return Err(InvokeError::ArgumentsNotMatched);
                }
                let mut iter = value.into_iter();

                $(
                    let $var = iter.next().ok_or(InvokeError::ArgumentsNotMatched)?;
                    let $var = $param::downcast($var).map_err(InvokeError::InputConvert)?;
                )*

                let r = self($($var),*).await.try_into();
                match r {
                    Ok(r) => Ok(Some(r)),
                    Err(e) => Err(InvokeError::InputConvert(e)),
                }
            }
        }
    };
}

macro_rules! impl_trait_ret_async_callable {
    ($c:expr, $name:ident, $($param:ident : $var:ident),*) => {
        paste! {
            #[allow(type_alias_bounds)]
            pub type [<TraitAsync $name>]<$($param,)* O> = fn($($param),*) -> Pin<Box<dyn Future<Output = O> + Send>>;


            #[async_trait]
            impl<$($param: Reflect,)* O: Reflect> TraitAsyncCallable for [<TraitAsync $name>]<$($param,)* O> {
                async fn async_call(&self, value: Vec<Value>) -> Result<Option<Object>, InvokeError> {
                    if value.len() != $c {
                        return Err(InvokeError::ArgumentsNotMatched);
                    }
                    let mut iter = value.into_iter();

                    $(
                        let $var = iter.next().ok_or(InvokeError::ArgumentsNotMatched)?;
                        let $var = $param::downcast($var).map_err(InvokeError::InputConvert)?;
                    )*

                    let r = self($($var),*).await.try_into();
                    match r {
                        Ok(r) => Ok(Some(r)),
                        Err(e) => Err(InvokeError::InputConvert(e)),
                    }
                }
            }
        }
    };
}

// 实现 Callable 特性
impl_ret_callable!(1, RetFn1, A1: a1);
impl_ret_callable!(2, RetFn2, A1: a1, A2: a2);
impl_ret_callable!(3, RetFn3, A1: a1, A2: a2, A3: a3);
impl_ret_callable!(4, RetFn4, A1: a1, A2: a2, A3: a3, A4: a4);
impl_ret_callable!(5, RetFn5, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5);
impl_ret_callable!(6, RetFn6, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6);
impl_ret_callable!(7, RetFn7, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7);
impl_ret_callable!(8, RetFn8, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8);
impl_ret_callable!(9, RetFn9, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8, A9: a9);
impl_ret_callable!(10, RetFn10, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8, A9: a9, A10: a10);
impl_ret_callable!(11, RetFn11, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8, A9: a9, A10: a10, A11: a11);
impl_ret_callable!(12, RetFn12, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8, A9: a9, A10: a10, A11: a11, A12: a12);
impl_ret_callable!(13, RetFn13, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8, A9: a9, A10: a10, A11: a11, A12: a12, A13: a13);
impl_ret_callable!(14, RetFn14, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8, A9: a9, A10: a10, A11: a11, A12: a12, A13: a13, A14: a14);
impl_ret_callable!(15, RetFn15, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8, A9: a9, A10: a10, A11: a11, A12: a12, A13: a13, A14: a14, A15: a15);
impl_ret_callable!(16, RetFn16, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8, A9: a9, A10: a10, A11: a11, A12: a12, A13: a13, A14: a14, A15: a15, A16: a16);

impl_ret_async_callable!(1, RetFn1, A1: a1);
impl_ret_async_callable!(2, RetFn2, A1: a1, A2: a2);
impl_ret_async_callable!(3, RetFn3, A1: a1, A2: a2, A3: a3);
impl_ret_async_callable!(4, RetFn4, A1: a1, A2: a2, A3: a3, A4: a4);
impl_ret_async_callable!(5, RetFn5, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5);
impl_ret_async_callable!(6, RetFn6, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6);
impl_ret_async_callable!(7, RetFn7, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7);
impl_ret_async_callable!(8, RetFn8, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8);
impl_ret_async_callable!(9, RetFn9, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8, A9: a9);
impl_ret_async_callable!(10, RetFn10, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8, A9: a9, A10: a10);
impl_ret_async_callable!(11, RetFn11, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8, A9: a9, A10: a10, A11: a11);
impl_ret_async_callable!(12, RetFn12, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8, A9: a9, A10: a10, A11: a11, A12: a12);
impl_ret_async_callable!(13, RetFn13, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8, A9: a9, A10: a10, A11: a11, A12: a12, A13: a13);
impl_ret_async_callable!(14, RetFn14, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8, A9: a9, A10: a10, A11: a11, A12: a12, A13: a13, A14: a14);
impl_ret_async_callable!(15, RetFn15, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8, A9: a9, A10: a10, A11: a11, A12: a12, A13: a13, A14: a14, A15: a15);
impl_ret_async_callable!(16, RetFn16, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8, A9: a9, A10: a10, A11: a11, A12: a12, A13: a13, A14: a14, A15: a15, A16: a16);

impl_trait_ret_async_callable!(1, RetFn1, A1: a1);
impl_trait_ret_async_callable!(2, RetFn2, A1: a1, A2: a2);
impl_trait_ret_async_callable!(3, RetFn3, A1: a1, A2: a2, A3: a3);
impl_trait_ret_async_callable!(4, RetFn4, A1: a1, A2: a2, A3: a3, A4: a4);
impl_trait_ret_async_callable!(5, RetFn5, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5);
impl_trait_ret_async_callable!(6, RetFn6, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6);
impl_trait_ret_async_callable!(7, RetFn7, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7);
impl_trait_ret_async_callable!(8, RetFn8, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8);
impl_trait_ret_async_callable!(9, RetFn9, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8, A9: a9);
impl_trait_ret_async_callable!(10, RetFn10, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8, A9: a9, A10: a10);
impl_trait_ret_async_callable!(11, RetFn11, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8, A9: a9, A10: a10, A11: a11);
impl_trait_ret_async_callable!(12, RetFn12, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8, A9: a9, A10: a10, A11: a11, A12: a12);
impl_trait_ret_async_callable!(13, RetFn13, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8, A9: a9, A10: a10, A11: a11, A12: a12, A13: a13);
impl_trait_ret_async_callable!(14, RetFn14, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8, A9: a9, A10: a10, A11: a11, A12: a12, A13: a13, A14: a14);
impl_trait_ret_async_callable!(15, RetFn15, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8, A9: a9, A10: a10, A11: a11, A12: a12, A13: a13, A14: a14, A15: a15);
impl_trait_ret_async_callable!(16, RetFn16, A1: a1, A2: a2, A3: a3, A4: a4, A5: a5, A6: a6, A7: a7, A8: a8, A9: a9, A10: a10, A11: a11, A12: a12, A13: a13, A14: a14, A15: a15, A16: a16);
